package com.xuecheng.orders.domain.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.base.exception.XueChengPlusException;
import com.xuecheng.base.mq.OrderTransDto;
import com.xuecheng.orders.domain.mapper.XcOrdersMapper;
import com.xuecheng.orders.domain.model.entity.MqMessage;
import com.xuecheng.orders.domain.model.entity.XcOrders;
import com.xuecheng.orders.domain.model.entity.XcOrdersGoods;
import com.xuecheng.orders.domain.model.entity.XcPayRecord;
import com.xuecheng.orders.domain.model.enums.AlipayEnum;
import com.xuecheng.orders.domain.model.enums.XcOrderStatusEnum;
import com.xuecheng.orders.domain.model.vo.req.OrderCreateCmd;
import com.xuecheng.orders.domain.model.vo.resp.PayRecordResp;
import com.xuecheng.orders.domain.service.MqMessageService;
import com.xuecheng.orders.domain.service.XcOrdersGoodsService;
import com.xuecheng.orders.domain.service.XcOrdersService;
import com.xuecheng.orders.domain.service.XcPayRecordService;
import com.xuecheng.orders.infrastructure.config.AlipayConfig;
import com.xuecheng.orders.infrastructure.util.QRCodeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.xuecheng.base.mq.MqConstant.MQ_EXCHANGE;
import static com.xuecheng.base.mq.MqConstant.MQ_ORDER_KEY;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class XcOrdersServiceImpl extends ServiceImpl<XcOrdersMapper, XcOrders> implements XcOrdersService {

    @Autowired
    private XcOrdersMapper xcOrdersMapper;

    @Autowired
    private XcOrdersGoodsService xcOrdersGoodsService;

    @Autowired
    private XcPayRecordService xcPayRecordService;

    @Autowired
    private MqMessageService mqMessageService;

    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Value("${qrcodeUrl}")
    private String qrcodeUrl;

    @Value("${alipay.appid}")
    private String appid;

    @Value("${alipay.appPrivateKey}")
    private String appPrivateKey;

    @Value("${alipay.alipayPublicKey}")
    private String alipayPublicKey;

    @Override
    @Transactional
    public PayRecordResp createOrder(Long userId, OrderCreateCmd orderCreateCmd) {
        // 创建课程订单
        XcOrders orderRecord = createOrderRecord(userId, orderCreateCmd);
        // 保存支付记录
        XcPayRecord payRecord = createPayRecord(orderRecord);
        // 生成二维码
        String url = genQRCodeUrl(payRecord);

        PayRecordResp payRecordResp = BeanUtil.copyProperties(payRecord, PayRecordResp.class);
        payRecordResp.setQrcode(url);

        return payRecordResp;
    }

    private String genQRCodeUrl(XcPayRecord payRecord) {
        String url = null;
        try {
            QRCodeUtil qrCodeUtil = new QRCodeUtil();
            url = qrCodeUtil.createQRCode(String.format(qrcodeUrl, payRecord.getPayNo().toString()), 200, 200);
        } catch (Exception e){
            log.error("生成二维码出错，{}", e.getMessage());
            throw new XueChengPlusException("生成二维码出错");
        }
        return url;
    }

    private XcPayRecord createPayRecord(XcOrders orderRecord) {
        XcPayRecord xcPayRecord = XcPayRecord.buildByOrder(orderRecord);
        xcPayRecordService.save(xcPayRecord);
        return xcPayRecord;
    }

    private XcOrders createOrderRecord(Long userId, OrderCreateCmd orderCreateCmd) {
        // todo 检查有效期内是否已经创建过订单了

        XcOrders xcOrders = XcOrders.buildByRequest(userId, orderCreateCmd);
        xcOrdersMapper.insert(xcOrders);

        // 保存订单商品
        List<XcOrdersGoods> xcOrdersGoods = JSONUtil.toList(xcOrders.getOrderDetail(), XcOrdersGoods.class)
                .stream()
                .map(item -> {
                    item.setOrderId(xcOrders.getId());
                    return item;
                }).collect(Collectors.toList());
        xcOrdersGoodsService.saveBatch(xcOrdersGoods);

        return xcOrders;
    }


    @Override
    public String requestPay(String payNo) {
        XcPayRecord one = xcPayRecordService.getOne(new LambdaQueryWrapper<XcPayRecord>()
                .eq(XcPayRecord::getPayNo, payNo));
        if (Objects.isNull(one)){
            throw new XueChengPlusException("订单不存在");
        }
        if (XcOrderStatusEnum.SUCCESS.getCode().equals(one.getStatus())) {
            throw new XueChengPlusException("订单已支付");
        }

        return getFormStr(one);
    }

    private String getFormStr(XcPayRecord one) {
        // 构建支付client
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, appid, appPrivateKey,
                AlipayConfig.FORMAT, AlipayConfig.CHARSET, alipayPublicKey, AlipayConfig.SIGNTYPE);
        // 支付请求
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setNotifyUrl("http://2fp7gk.natappfree.cc/orders/receivenotify");
//        request.setReturnUrl("");
        JSONObject jsonObject = new JSONObject();
        jsonObject.set("out_trade_no", one.getPayNo().toString());
        jsonObject.set("total_amount", one.getTotalPrice().toString());
        jsonObject.set("subject", one.getOrderName().toString());
        jsonObject.put("product_code", "FAST_INSTANT_TRADE_PAY");
        request.setBizContent(jsonObject.toString());
        String formStr;
        try {
            AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
            if (!response.isSuccess()){
                throw new XueChengPlusException("创建支付订单失败");
            }
            formStr = response.getBody();
        } catch (Exception e){
            log.error("创建支付订单出错，{}", e.getMessage());
            throw new XueChengPlusException("创建支付订单出错");
        }
        return formStr;
    }

    @Override
    public void receivenotify(Map<String, String> params) {
        String tradeNo = params.get("trade_no");
        String tradeStatus = params.get("trade_status");
        String outTradeNo = params.get("out_trade_no");
        XcPayRecord one = xcPayRecordService.getOne(new LambdaQueryWrapper<XcPayRecord>()
                .eq(XcPayRecord::getPayNo, outTradeNo));
        if (XcOrderStatusEnum.SUCCESS.getCode().equals(one.getStatus())){
            throw new XueChengPlusException("订单已经支付");
        }
        if (AlipayEnum.SUCCESS.getCode().equals(tradeStatus)){
            // 支付成功
            one.setStatus(XcOrderStatusEnum.SUCCESS.getCode());
            one.setOutPayChannel(tradeNo);
            one.setPaySuccessTime(LocalDateTime.now());
            xcPayRecordService.updateById(one);

            // 保存到消息表中，防止重复支付
            mqMessageService.saveMqMessage(MqMessage.buildByPayRecord(one));
            // 发送到mq，通知学习服务，保存选课
            sendMqMsg(one);


        }

    }

    private void sendMqMsg(XcPayRecord one) {
        Long orderId = one.getOrderId();
        XcOrders xcOrders = xcOrdersMapper.selectById(orderId);

        List<OrderTransDto.CourseInfo> collect = JSONUtil.toList(xcOrders.getOrderDetail(), XcOrdersGoods.class)
                .stream()
                .map(item -> {
                    OrderTransDto.CourseInfo courseInfo = new OrderTransDto.CourseInfo();
                    courseInfo.setCourseId(Long.parseLong(item.getGoodsId()));
                    courseInfo.setCourseType(item.getGoodsType());
                    courseInfo.setCourseName(item.getGoodsName());
                    courseInfo.setGoodsPrice(item.getGoodsPrice());
                    return courseInfo;
                })
                .collect(Collectors.toList());
        rabbitTemplate.convertAndSend(MQ_EXCHANGE, MQ_ORDER_KEY, new OrderTransDto(collect, Long.parseLong(xcOrders.getUserId())));
    }
}
